package 
{
	import collisions.LinkedList;
	import collisions.World;
	import com.demonsters.debugger.MonsterDebugger;
	import enemies.Enemy;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import graphics.GraphicsFactory;
	import player.IController;
	import player.Player;
	import recorder.EntityPositionRecorder;
	import recorder.IRecorder;
	import renderers.EntityBitmapRenderer;
	import renderers.EntityRenderer;
	import renderers.Renderer;	
	
	/**
	 * ...
	 * @author Gareth Williams
	 */
	public class Main extends Sprite 
	{
		private var gamePad:IController;
		
	//profiling
		private static var totalBodies:int = 0;
		
		//these all need to be static for access from static interface
		private static var world:World = new World();
		private static var display:Renderer;
		
		private static const goodGuyHead:LinkedList = new LinkedList(null);
		private static var goodGuyTail:LinkedList = goodGuyHead;
		private static const badGuyHead:LinkedList = new LinkedList(null);
		private static var badGuyTail:LinkedList = badGuyHead;
		private static const bulletHead:LinkedList = new LinkedList(null);
		private static var bulletTail:LinkedList = bulletHead;
		
		public static var timeIncrement:Number = 1.0;
		public static var timestep:Number = 0;//use a number for variable speeds slowmo etc
		
		//debug
		private var isForwards:Boolean = true;
		private var HUD:TextField = new TextField();
		
		public function Main():void 
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		public static function addBullet(b:Bullet):void 
		{
			b.graphic = GraphicsFactory.createGraphic(GraphicsFactory.GREEN_CIRCLE, b.radius, b.radius);
			bulletTail.next = new LinkedList(b);
			bulletTail = bulletTail.next;
			World.addBody(b);
			display.addRenderable(b);
		}
		public static function createBadGuy():void
		{
			var badGuy:Enemy = new Enemy(Math.random() * Constants.SCREEN_WIDTH, Math.random() * Constants.SCREEN_HEIGHT);
			badGuy.graphic = GraphicsFactory.createGraphic(
				GraphicsFactory.BLUE_SQUARE, badGuy.radius, badGuy.radius);	
			World.addBody(badGuy);
			display.addRenderable(badGuy);
			badGuyTail.next = new LinkedList(badGuy);
			badGuyTail = badGuyTail.next;
		}
		private function init(e:Event = null):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			// entry point
		
			MonsterDebugger.initialize(this);
			
			//test----------------------------------------------------------------------------
				
				//display = new EntityRenderer(this.stage);
				display = new EntityBitmapRenderer(this.stage);
				
				//setup proxy so that player is driven by function of timestep rather than directly by player
				gamePad = new Gamepad(this.stage);
				this.spawnGoodGuy();
				
				//debug keys & timestep
				stage.addEventListener(KeyboardEvent.KEY_UP, this.onKeyRelease);
				stage.addChild(HUD);
				HUD.defaultTextFormat = new TextFormat(null, 24, 0xFFFFFF);
				//World.debugDraw = new Sprite();
				//stage.addChild(World.debugDraw);
				
			//test----------------------------------------------------------------------------
			
			//main loop
			addEventListener(Event.ENTER_FRAME, this.main);
		}
		private function spawnGoodGuy():void
		{
		
	//profiling
			trace("-------spawn--------");
			
			if (null !== goodGuyTail.object)
			{
				Player(goodGuyTail.object).controller = null;
			}
			
			//setup player
			var goodGuy:Player = new Player(Constants.SCREEN_WIDTH / 2, Constants.SCREEN_HEIGHT / 2, 20);
			goodGuy.graphic = GraphicsFactory.createGraphic(
				GraphicsFactory.RED_SQUARE, goodGuy.radius, goodGuy.radius);
				
			var driver:IRecorder = new EntityPositionRecorder(goodGuy);
			Player(goodGuy).controller = gamePad;
			Player(goodGuy).driver = driver;
			
			//add to world
			World.addBody(goodGuy);
			display.addRenderable(goodGuy);
			
			//add to vector
			goodGuyTail.next = new LinkedList(goodGuy);
			goodGuyTail = goodGuyTail.next;	
		}
		private function main(event:Event):void
		{
		//profiling
			totalBodies = 0;
			
			//update player
			gamePad.update();
			
			//update good guys
			var goodGuyIterator:LinkedList = goodGuyHead;
			while (goodGuyIterator.next)
			{
				goodGuyIterator = goodGuyIterator.next;
				var currentGoodGuy:Player = Player(goodGuyIterator.object);
				//The order these get called is *very* important
				currentGoodGuy.update();
				currentGoodGuy.driver.update();
		//profiling
				totalBodies++;
			}
			//update bad guys
			var badGuyIterator:LinkedList = badGuyHead;
			while (badGuyIterator.next)
			{
				badGuyIterator = badGuyIterator.next;
				var currentBadGuy:Enemy = Enemy(badGuyIterator.object);
				currentBadGuy.update();
		//profiling
				totalBodies++;
			}
			//update bullets
			var bulletIterator:LinkedList = bulletHead;
			while (bulletIterator.next)
			{
				bulletIterator = bulletIterator.next;
				var currentBullet:Bullet = Bullet(bulletIterator.object);
				currentBullet.update();
		//profiling
				totalBodies++;
			}
			
			//update global
			world.update();
			display.render();
			
			//debug add random badguys
			if (Math.random() > 0.9 && isForwards) createBadGuy();
			
			//timestep
			Main.timestep += this.isForwards ? +timeIncrement : -timeIncrement;
			
			//debug display timestep
			HUD.text = String(timestep);
			
		//profiling
			trace("bodies in memory:", totalBodies);
		}
		//debug
		private function onKeyRelease(key:KeyboardEvent):void
		{
			if (key.keyCode == 82)
			{
				Main.timestep = 0;
				this.spawnGoodGuy();
			}
			if (key.keyCode == 69)
			{
				this.isForwards = !this.isForwards;
			}
			if (key.keyCode == 81)
			{
				Main.timeIncrement -= 0.1;
			}
			if (key.keyCode == 87)
			{
				Main.timeIncrement += 0.1;
			}
		}
	}
}